#include "MeshUtil.h"
#include "Mesh.h"
#include "../../Helix.h"
#include "../../HObject.h"
#include "../../Util/StringUtil.h"

#include <vector>
#include <string>
#include <iostream>
#include <fstream>


using namespace std;

namespace helix
{

	Mesh *MeshUtil::loadFromFile(const char *fileName)
	{
		HObject::logger().Println("MeshUtil:: Loading Mesh from %s...",fileName);

		string fName = fileName;
		size_t extPos = fName.find_last_of('.');
		if(extPos == fName.size() || extPos == fName.size()-1)
		{
			HObject::logger().Error("MeshUtil:: Could not load Mesh without an extension.");
			return NULL;
		}

		string ext = fName.substr(extPos+1);

		Mesh *ret = NULL;	//mesh to return 
		if(StringUtil::compareIgnoreCase(&ext,&string("hmf")) == 0)			//Is this a Helix Model
		{
			ret = loadFromHMF(fileName);
			if(ret==NULL)
			{
				return NULL;	
			}
		}
		else																//Extension not supported
		{
			HObject::logger().Error("MeshUtil:: Could not load Mesh. Extension \"%s\" not supported.",ext.c_str());
			return NULL;
		}

		HObject::logger().Println("MeshUtil:: ...successfully loaded Mesh.");
		return ret;
	}


	Mesh *MeshUtil::loadFromHMF(const char *fileName)
	{
		char line[1024];	//storage for each line as we read it in
		int currentLine = 0;

		//Open the file for reading (text mode)
		ifstream fileIn(fileName,ifstream::in);

		//Make sure the file is open
		if(!fileIn.is_open())
		{
			HObject::logger().Error("File: %s did not open correctly. ",fileName);
			fileIn.close();
			return NULL;
		}

		getNextLine(&fileIn,line,&currentLine);
		char H,M,F;
        int itemsRead = sscanf(line,"%c%c%c",&H,&M,&F);
		//The first 3 characters of the file should be "HMF" and there can be anything after that on the first line
		if(itemsRead!=3 || H!='H' || M!='M' || F!='F')
		{
			HObject::logger().Error("File: %s is not a valid Helix Model File. ",fileName);
			fileIn.close();
			return NULL;
		}

		//get the major version of the file
		getNextLine(&fileIn,line,&currentLine);
		int majorVersion;
		itemsRead = sscanf(line,"Major Version: %d",&majorVersion);

		//get the minor version of the file
		getNextLine(&fileIn,line,&currentLine);
		int minorVersion;
		itemsRead = sscanf(line,"Minor Version: %d",&minorVersion);

		//get the number of vertices per face for the mesh
		getNextLine(&fileIn,line,&currentLine);
		int verticesPerFace;
		itemsRead = sscanf(line,"Vertices Per Face: %d",&verticesPerFace);

		//get the number of faces in the mesh
		getNextLine(&fileIn,line,&currentLine);
		int faces;
		itemsRead = sscanf(line,"Faces: %d",&faces);

		//get the number of vertices in the mesh
		getNextLine(&fileIn,line,&currentLine);
		int vertices;
		itemsRead = sscanf(line,"Vertices: %d",&vertices);

		//get the number of user-defined buffers in the mesh
		getNextLine(&fileIn,line,&currentLine);
		int userBuffers;
		itemsRead = sscanf(line,"User-defined Buffers: %d",&userBuffers);

		if(verticesPerFace<=0 || faces<=0 || vertices<=0 || userBuffers<0 || !fileIn.good())
		{
			HObject::logger().Error("Invalid header data. \n"
									"    Vertices Per Face = %d\n"
									"    Faces = %d\n"
									"    Vertices = %d\n"
									"    User-defined Buffers = %d",
									verticesPerFace,faces,vertices,userBuffers);
			fileIn.close();
			return NULL;
		}

		getNextLine(&fileIn,line,&currentLine);
		itemsRead = sscanf(line,"Start Face Data:");
		if(itemsRead == EOF)
		{
			HObject::logger().Error("No \"Start Face Data\" section found.");
			fileIn.close();
			return NULL;
		}

		//Read in face data
		vector<unsigned int> faceData = vector<unsigned int>();
		for(int i=0;i<faces;i++)
		{
			if(!getNextLine(&fileIn,line,&currentLine))
			{
				HObject::logger().Error("Unexpected end of file.  Line: %i",currentLine);
				fileIn.close();
				return NULL;
			}

			string formatStr = "%u";
			for(int j=0;j<verticesPerFace;j++)
			{
				int faceDatIndex;
				sscanf(line,formatStr.c_str(),&faceDatIndex);
				faceData.push_back(faceDatIndex);

				formatStr = "%*s " + formatStr;
			}
		}


		getNextLine(&fileIn,line,&currentLine);
		itemsRead = sscanf(line,"Start Vertex Data:");
		if(itemsRead == EOF)
		{
			HObject::logger().Error("No \"Start Vertex Data\" section found.");
			fileIn.close();
			return NULL;
		}

		//Read in vertex data
		vector<float> vertData = vector<float>();
		for(int i=0;i<vertices;i++)
		{
			if(!getNextLine(&fileIn,line,&currentLine))
			{
				HObject::logger().Error("Unexpected end of file.  Line: %d",currentLine);
				fileIn.close();
				return NULL;
			}

			float x,y,z;
			sscanf(line,"%f %f %f",&x,&y,&z);
			vertData.push_back(x);
			vertData.push_back(y);
			vertData.push_back(z);
		}

		//Create the new Mesh object
		Mesh *mesh = new Mesh(verticesPerFace,verticesPerFace*faces,&faceData[0],vertices*3,&vertData[0]);


		//Read in all the user-defined buffers
		for(int i=0;i<userBuffers;i++)
		{
			//Read in the name of the buffer
			char bufferName[256];
			getNextLine(&fileIn,line,&currentLine);
			itemsRead = sscanf(line,"User-defined Buffer: %s",bufferName);
			if(itemsRead == EOF)
			{
				HObject::logger().Error("No \"User-defined Buffer\" section found. Failed on %d of %d buffer.",i+1,userBuffers);
				fileIn.close();
				delete mesh;
				return NULL;
			}
		
			//Read in the number of float values per vertex that are in the buffer
			int partsPerVertex;
			getNextLine(&fileIn,line,&currentLine);
			itemsRead = sscanf(line,"Parts Per Vertex: %d",&partsPerVertex);
			if(itemsRead == EOF || itemsRead == 0)
			{
				HObject::logger().Error("No specified parts per vertex for buffer %s. Failed on %d of %d buffer.",bufferName,i+1,userBuffers);
				fileIn.close();
				delete mesh;
				return NULL;
			}

			//Read in buffer data
			vector<float> buffData = vector<float>();
			for(int j=0;j<vertices;j++)
			{
				if(!getNextLine(&fileIn,line,&currentLine))
				{
					HObject::logger().Error("Unexpected end of file.  Line: %d",currentLine);
					fileIn.close();
					delete mesh;
					return NULL;
				}

				string buffFormatStr = "%f";
				for(int k=0;k<partsPerVertex;k++)
				{
					float buffDatPart;
					sscanf(line,buffFormatStr.c_str(),&buffDatPart);
					buffData.push_back(buffDatPart);

					buffFormatStr = "%*s " + buffFormatStr;
				}
			}

			//Determine what type of buffer it was, and store it in the Mesh object
			//accordingly
			if(strcmp(bufferName,"TEXTURE_COORDINATE_DATA")==0)
			{
				int texCoordType = 0;
				if(partsPerVertex == 1)
					texCoordType = Mesh::TEX_COORD_1D_BUFFER;
				else if(partsPerVertex == 2)
					texCoordType = Mesh::TEX_COORD_2D_BUFFER;
				else if(partsPerVertex == 3)
					texCoordType = Mesh::TEX_COORD_3D_BUFFER;
				if(texCoordType !=0)
					mesh->attachDataBuffer(&buffData[0],texCoordType);
				else
					mesh->attachCustomDataBuffer(&buffData[0],partsPerVertex,bufferName);
			}
			else if(strcmp(bufferName,"NORMAL_DATA")==0)
			{
				mesh->attachDataBuffer(&buffData[0],Mesh::NORMAL_BUFFER);
			}
			else if(strcmp(bufferName,"VERTEX_COLOR_DATA")==0)
			{
				int colFormat = 0;
				if(partsPerVertex == 3)
					colFormat = Mesh::RGB_COLOR_BUFFER;
				else if(partsPerVertex == 4)
					colFormat = Mesh::RGBA_COLOR_BUFFER;
				if(colFormat !=0)
					mesh->attachDataBuffer(&buffData[0],colFormat);
				else
					mesh->attachCustomDataBuffer(&buffData[0],partsPerVertex,bufferName);
			}
			else if(strcmp(bufferName,"TANGENT_DATA")==0)
			{
				mesh->attachDataBuffer(&buffData[0],Mesh::TANGENT_BUFFER);
			}
			else if(strcmp(bufferName,"BITANGENT_DATA")==0)
			{
				mesh->attachDataBuffer(&buffData[0],Mesh::BITANGENT_BUFFER);
			}
			else
			{
				mesh->attachCustomDataBuffer(&buffData[0],partsPerVertex,bufferName);
			}
		}


		//Close the file and return the mesh object
		fileIn.close();
		return mesh;
	}

	bool MeshUtil::getNextLine(ifstream *inFile,char *buff,int *currentLine)
	{
		bool validLine = false;
		while(!validLine && inFile->good())
		{
			(*currentLine)++;
			inFile->getline(buff,1024);
			char firstStr[3]; //room for 2 characters and a null character
			int readItems = sscanf(buff,"%2s",firstStr);

			//If there is something on the line, then it could be valid
			if(readItems == 1)
			{
				//If the line doesn't start with # character, then it's valid
				if(firstStr[0] != '#')
					validLine = true;
			}
		}

		return validLine;
	}

	void MeshUtil::deleteMesh(Mesh *mesh)
	{
		if(mesh != NULL)
			delete mesh;
	}

	void MeshUtil::scaleVertices(Mesh *mesh,float scaleFactor)
	{
		
		vector<float> * verts = mesh->getVertData();

		vector<float>::size_type numVerts = verts->size();
		
		for(int i=0;i<(int)numVerts;i++)
		{
			(*verts)[i] = (*verts)[i] * scaleFactor;
		}
	}

}